package com.nekrashevych;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;

import com.nekrashevych.itm.GenerationTagLiterals;
import com.nekrashevych.util.MathingNameFileFilter;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;

public class Merger {

    private Configuration configuration;
    private String fromRoot;
    private String toRoot;
    private Map<String, JavaClass> allBeans;
    private Set<String> imports = new HashSet<String>();
    private boolean SKIP_FILES_COPY = true;
    private boolean SKIP_BEAN_MERGE = true;

    public void merge() throws Exception {
        configuration = new PropertiesConfiguration("location.properties");
        fromRoot = configuration.getString("sourceFolder");
        toRoot = configuration.getString("destinationFolder");
        SKIP_FILES_COPY = configuration.getBoolean("SKIP_FILES_COPY");
        SKIP_BEAN_MERGE = configuration.getBoolean("SKIP_BEAN_MERGE");

        Collection<File> beans = FileUtils.listFiles(new File(fromRoot, "src/java/com/itmsoft/ejbs/"),
                FileFilterUtils.and(FileFilterUtils.suffixFileFilter("Bean.java")), FileFilterUtils.trueFileFilter());
        allBeans = new HashMap<String, JavaClass>();
        for (File f : beans) {
            JavaClass clazz = MergeUtils.getJavaClass(f);
            allBeans.put(clazz.getName(), clazz);
        }
        allBeans = Collections.unmodifiableMap(allBeans);
        System.err.println("-----------------------------------------------------");
        System.err.println("Processing beans");
        System.err.println("-----------------------------------------------------");
        for (File f : beans) {
            if (SKIP_BEAN_MERGE) {
                break;
            }
            JavaClass clazz = MergeUtils.getJavaClass(f);
            if (clazz.getTagByName("ejbgen:message-driven") != null) {
                processMDBs(f);
            }
            if (clazz.getTagByName("ejbgen:session") != null) {
                processSession(f);
            }
            if (clazz.getTagByName("ejbgen:entity") != null) {
                processEntity(f);
            }
        }
        System.err.println("Done");
        copyFiles();
    }

    private void copyFiles() throws Exception {
        if (SKIP_FILES_COPY) {
            return;
        }
        IOFileFilter java = FileFilterUtils.suffixFileFilter(".java");
        IOFileFilter packageInfo = FileFilterUtils.suffixFileFilter("package.html");
        IOFileFilter generator = FileFilterUtils.suffixFileFilter("Generator.java");
        MathingNameFileFilter test = new MathingNameFileFilter("Test");

        IOFileFilter javaFileFilter = FileFilterUtils.and(FileFilterUtils.fileFileFilter(), FileFilterUtils.or(java, packageInfo),
                FileFilterUtils.notFileFilter(generator), FileFilterUtils.notFileFilter(test));

        IOFileFilter readmeTxt = FileFilterUtils.suffixFileFilter("README.txt");
        IOFileFilter resources = FileFilterUtils.and(FileFilterUtils.fileFileFilter(),
                FileFilterUtils.notFileFilter(FileFilterUtils.or(java, readmeTxt, packageInfo)));

        IOFileFilter tests = FileFilterUtils.and(FileFilterUtils.fileFileFilter(), FileFilterUtils.and(java, FileFilterUtils.or(test, generator)));

        copyFiles(javaFileFilter, "src/java/com/itmsoft/web", new File(Projects.WEB.getJavaSourceFolder(toRoot), "/com/itmsoft/web"),
                FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter("dataGenerator")));
        copyFiles(javaFileFilter, "src/java/javachart/applet", new File(Projects.WEB.getJavaSourceFolder(toRoot), "/javachart/applet"));
        copyFiles(javaFileFilter, "src/java/javachart/chart", new File(Projects.WEB.getJavaSourceFolder(toRoot), "/javachart/chart"));
        copyFiles(resources, "src/java/com/itmsoft/web", new File(Projects.WEB.getJavaResourceFolder(toRoot), "/com/itmsoft/web"));
        copyFiles(resources, "src/java/javachart/applet", new File(Projects.WEB.getJavaResourceFolder(toRoot), "/javachart/applet"));
        copyFiles(resources, "src/java/javachart/chart", new File(Projects.WEB.getJavaResourceFolder(toRoot), "/javachart/chart"));
        System.err.println("Copying web resources....");
        FileUtils.copyDirectory(new File(fromRoot, "src/web"), new File(Projects.WEB.getProjectFolder(toRoot), "/src/webapp"));
        System.err.println("Done.");
        // copyFiles(resources, "src/web", new
        // File(Projects.WEB.getProjectFolder(toRoot), "/src/webapp"));
        copyFiles(tests, "src/java/com/itmsoft/web", new File(Projects.WEB.getJavaTestSourcesFolder(toRoot), "/com/itmsoft/web"));

        // common files
        copyFiles(javaFileFilter, "src/java/com/itmsoft/common", new File(Projects.COMMON.getJavaSourceFolder(toRoot), "/com/itmsoft/common"));
        copyFiles(resources, "src/java/com/itmsoft/common", new File(Projects.COMMON.getJavaResourceFolder(toRoot), "/com/itmsoft/common"));
        copyFiles(javaFileFilter, "src/java/com/itmsoft/modules/itsm/change", new File(Projects.COMMON.getJavaSourceFolder(toRoot),
                "/com/itmsoft/modules/itsm/change"));
        copyFiles(resources, "src/java/com/itmsoft/modules/itsm/change", new File(Projects.COMMON.getJavaResourceFolder(toRoot),
                "/com/itmsoft/modules/itsm/change"));
        copyFiles(javaFileFilter, "src/java/com/itmsoft/modules/itsm/release", new File(Projects.COMMON.getJavaSourceFolder(toRoot),
                "/com/itmsoft/modules/itsm/release"));
        copyFiles(resources, "src/java/com/itmsoft/modules/itsm/release", new File(Projects.COMMON.getJavaResourceFolder(toRoot),
                "/com/itmsoft/modules/itsm/release"));

        // documentation
        copyFiles(FileFilterUtils.suffixFileFilter("itbm_localhelp.zip"), "documentation",
                new File(Projects.DOCS.getProjectFolder(toRoot), "src/zip"));

        // ejbs (excluding the *Bean.java files that are processed above)
        copyFiles(FileFilterUtils.and(javaFileFilter, FileFilterUtils.notFileFilter(FileFilterUtils.suffixFileFilter("Bean.java"))),
                "src/java/com/itmsoft/ejbs", new File(Projects.SERVICES.getJavaSourceFolder(toRoot), "/com/itmsoft/ejbs"));
        copyFiles(resources, "src/java/com/itmsoft/ejbs", new File(Projects.SERVICES.getJavaResourceFolder(toRoot), "/com/itmsoft/ejbs"));

        // web services:
        copyFiles(javaFileFilter, "src/java/com/itmsoft/webservice", new File(Projects.WS.getJavaSourceFolder(toRoot), "/com/itmsoft/webservice"));
        copyFiles(resources, "src/java/com/itmsoft/webservice", new File(Projects.WS.getJavaResourceFolder(toRoot), "/com/itmsoft/webservice"));
        // copyFiles(resources, "src/webservice", new
        // File(Projects.WS.getProjectFolder(toRoot), "/src/webapp"));
        System.err.println("Copying webservices resources....");
        FileUtils.copyDirectory(new File(fromRoot, "src/webservice"), new File(Projects.WS.getProjectFolder(toRoot), "/src/webapp"));
        System.err.println("Done.");

        // build files:
        copyFiles(FileFilterUtils.suffixFileFilter("ehcache.xml"), "src/build", new File(Projects.SERVICES.getJavaTestSourcesFolder(toRoot)));

        // TODO other build files
    }

    public void copyFiles(IOFileFilter fileFilter, String sourceFolderAddOn, File dest) throws Exception {
        copyFiles(fileFilter, sourceFolderAddOn, dest, FileFilterUtils.trueFileFilter());
    }

    public void copyFiles(IOFileFilter fileFilter, String sourceFolderAddOn, File dest, IOFileFilter folderFileFilter) throws Exception {
        File source = new File(fromRoot, sourceFolderAddOn);
        System.err.println("-----------------------------------------------------");
        System.err.printf("Processing folder %s ----> %s (filter: %s)\n", source.getAbsolutePath(), dest.getAbsolutePath(), fileFilter.toString());
        System.err.println("-----------------------------------------------------");
        Thread.sleep(100);
        Collection<File> files = FileUtils.listFiles(source, fileFilter, folderFileFilter);
        System.out.printf("About to copy: %s files\n", files.size());
        for (File f : files) {
            // System.out.printf(" %s \n",
            // StringUtils.remove(f.getAbsolutePath(),
            // source.getAbsolutePath()));
            FileUtils.copyFile(f, new File(dest.getAbsolutePath(), StringUtils.remove(f.getAbsolutePath(), source.getAbsolutePath())));
        }
        System.out.printf("Done\n");
        Thread.sleep(100);
    }

    private void processEntity(File f) throws Exception {
        MergeUtils.copyAndReplaceContents(
                new ContentReplacer() {
                    @Override
                    public String manipulate(JavaClass clazz, String string) {
                        return manipulateEntity(clazz, string);
                    }
                },
                f,
                new File(Projects.MT_DTO.getJavaSourceFolder(toRoot), StringUtils.substringAfter(StringUtils.remove(f.getAbsolutePath(), "Bean"),
                        "src\\java\\")));
    }

    protected String manipulateEntity(JavaClass clazz, String string) {
        imports.clear();
        String result = MergeUtils.removeJavadocs(string);
        // removes everything before class and after last ";"
        // (shuld be
        // after import statement)
        result = StringUtils.remove(result, StringUtils.substringAfterLast(StringUtils.substringBefore(result, "class"), ";"));
        result = StringMergeUtil.insert(result, "class", "\npublic ");
        result = StringMergeUtil.insertLine(result, "public class", "import javax.annotation.*;");
        result = StringMergeUtil.insertLine(result, "public class", "import javax.persistence.*;\n");
        result = StringUtils.replace(result, clazz.getName(), StringUtils.remove(clazz.getName(), "Bean"));
        result = MergeUtils.removeCloverTags(result);
        // remove contents
        result = StringUtils.remove(result, StringUtils.substringBeforeLast(StringUtils.substringAfter(result, "{"), "}"));
        result = StringUtils.remove(result, "abstract");
        result = StringUtils.remove(result, "implements EntityBean");
        result = StringMergeUtil.insert(result, "}", "\n");
        imports.add("java.util.Set");
        imports.add("java.util.Date");
        List<String> fields = new ArrayList<String>();
        List<String> itmFields = new ArrayList<String>();
        // -----------------------
        // -------annotations-----
        // -----------------------
        // fields and relations:
        for (JavaMethod m : clazz.getMethods()) {
            // fields
            if (!m.getName().startsWith("get")) {
                continue;
            }
            String variableName = WordUtils.uncapitalize(StringUtils.substring(m.getName(), 3));
            String returnType = m.getReturns().getJavaClass().getName();
            if (!MergeUtils.SIMPLE_TYPES.contains(returnType) && !MergeUtils.SIMPLE_TYPES.contains(StringUtils.substringAfterLast(returnType, "."))
                    && !returnType.equals("Collection")) {
                imports.add(returnType);
            }
            if (returnType.contains(".")) {
                returnType = StringUtils.substringAfterLast(returnType, ".");
            }

            String joined = processItmFields(itmFields, m);
            if (!joined.isEmpty()) {
                fields.add(String.format("@ItmField(%s)", joined));
            }
            if (m.getTagByName(GenerationTagLiterals.ITM_DTO_FIELD_TYPE) != null) {
                fields.add(MergeUtils.ifMethodTagValues(m, GenerationTagLiterals.ITM_DTO_FIELD_TYPE, "@ItmDtoFieldType(%s)"));
            }
            fields.add(MergeUtils.ifMethodTagValue(m, GenerationTagLiterals.EJBGEN_PRIMKEY_FIELD, "@Id"));

            fields.addAll(getRelByType(clazz, variableName, returnType, "ejbgen:relation"));
            fields.addAll(getRelByType(clazz, variableName, returnType, "itm:relation"));

            if (returnType.equals("Timestamp")) {
                fields.add(String.format("@Temporal(TemporalType.TIMESTAMP)"));
                returnType = "Date";
            }

            if (MergeUtils.SIMPLE_TYPES.contains(returnType)) {
                if (m.getTagByName("ejbgen:cmp-field") != null && m.getTagByName("ejbgen:cmp-field").getNamedParameter("column") != null) {
                    String column = m.getTagByName("ejbgen:cmp-field").getNamedParameter("column");
                    if (!StringMergeUtil.toColumnUnderscores(variableName).equalsIgnoreCase(column)) {
                        fields.add(String.format("@Column(name = \"%s\")", column));
                    }
                }
            }
            if (!"Collection".equals(returnType)) {
                fields.add("private " + returnType + " " + variableName + ";\n");
            }

        }
        result = StringMergeUtil.insert(result, "}", Constants.TAB + StringMergeUtil.joinWithoutNulls(fields, "\n" + Constants.TAB) + "\n\n");
        for (String imp : imports) {
            result = StringMergeUtil.insert(result, "import ", "import " + imp + ";\r\n");
        }
        // -----------------------
        // -------annotations-----
        // -----------------------
        // finders:
        List<String> queries = new ArrayList<String>();
        if (clazz.getTagsByName("ejbgen:finder").length != 0) {
            result = StringMergeUtil.insertLine(result, "public class", "@NamedQueries({");
            for (DocletTag finder : clazz.getTagsByName("ejbgen:finder")) {
                StringBuilder query = new StringBuilder("@NamedQuery(name = \"");
                List<String> hints = new ArrayList<String>();
                query.append(StringUtils.remove(clazz.getName(), "Bean")).append(".");
                query.append(StringUtils.substringBetween(finder.getNamedParameter("signature"), " ", "("));
                query.append("\"");
                query.append(String.format(", query = \"%s\"", finder.getNamedParameter("ejb-ql")));
                hints.add(String.format("@QueryHint(name = \"%s\", value = \"%s\")", "signature", finder.getNamedParameter("signature")));
                if (finder.getNamedParameter("dto") != null) {
                    hints.add(String.format("@QueryHint(name = \"%s\", value = \"%s\")", "dto", finder.getNamedParameter("dto")));
                }
                query.append(String.format(", hints={%s}", StringUtils.join(hints, ",")));
                query.append(")");
                queries.add(query.toString());
            }
            result = StringMergeUtil.insertLine(result, "public class", StringUtils.join(queries, ",\n"));
            result = StringMergeUtil.insertLine(result, "public class", "})");
        }
        // table name
        result = StringMergeUtil.insertLine(result, "public class",
                String.format("@Table(name=\"%s\")", clazz.getTagByName("ejbgen:entity").getNamedParameter("table-name")));
        // entity start
        result = StringMergeUtil.insert(result, "public class", "@ItmEntity(");
        List<String> params = new ArrayList<String>();

        if (clazz.getTagByName(GenerationTagLiterals.ITM_FACTORY_NAME) != null) {
            params.add(String.format("factory=\"%s\"", clazz.getTagByName(GenerationTagLiterals.ITM_FACTORY_NAME).getValue()));
        }

        if (clazz.getTagByName(GenerationTagLiterals.ITM_DTO) != null) {
            params.add(String.format("dto={%s}",
                    StringMergeUtil.splitToArray(clazz.getTagByName(GenerationTagLiterals.ITM_DTO).getNamedParameter("name"))));
        }

        if (clazz.getTagByName(GenerationTagLiterals.ITM_COMPOSITE_SORT) != null) {
            params.add(String.format("compositeSortProperty=\"%s\"",
                    clazz.getTagByName(GenerationTagLiterals.ITM_COMPOSITE_SORT).getNamedParameter("value")));
        }
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_CACHEABLE_OBJ, "cacheable=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_EVENT, "event=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_FLEX, "flex=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_ALIAS_ENTITY, "aliasEntity=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_ACTION_CLASS, "actionClass=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_CACHE_BY_PK_ONLY, "cacheByPk=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_CACHE_BY_PK_AND_LOCALE, "cacheByPkAndLocale=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_GUID, "guid=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_CIRCULAR_OBJECT, "circular=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_TOP_LEVEL_OBJECT, "topLevel=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_MULTI_TENANT, "multiTenant=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_GET_LIST_OPTIMIZATION, "listOptimization=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_IMPORTABLE_OBJECT, "importable=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_IMPORTABLE_XML_OBJECT, "xmlImportable=true"));
        params.add(MergeUtils.ifTag(clazz, "itm:skipTest", "skipTest=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_LOCALE_IMPLEMENTED_OBJECT, "implementsLocale=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_SOFT_DELETE, "softDelete=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_NO_DATAACCESS_CONTROL, "noDataAccessControl=true"));
        params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_SEARCHABLE_OBJECT, "searchable=true"));

        if (clazz.getTagByName(GenerationTagLiterals.ITM_LOCALE_IMPLEMENTED) != null) {
            params.add(String.format("localeImplemented=\"%s\"", clazz.getTagByName(GenerationTagLiterals.ITM_LOCALE_IMPLEMENTED).getValue()));
        }

        if (clazz.getTagByName(GenerationTagLiterals.ITM_SECURITY_LITERAL) != null) {
            params.add(String.format("securityLiteral=\"%s\"", clazz.getTagByName(GenerationTagLiterals.ITM_SECURITY_LITERAL).getValue()));
        }

        if (clazz.getTagByName(GenerationTagLiterals.ITM_XSD_OUTPUT_ORDER) != null) {
            List<String> tmp = new ArrayList<String>();
            for (String order : StringUtils.split(clazz.getTagByName(GenerationTagLiterals.ITM_XSD_OUTPUT_ORDER).getNamedParameter("name"), ",")) {
                tmp.add(StringUtils.uncapitalize(StringUtils.remove(order, "get")));
            }
            params.add(String.format("xsdOutputOrder={%s}", StringMergeUtil.splitToArray(StringUtils.join(tmp, ","))));
        }

        if (clazz.getTagByName(GenerationTagLiterals.ITM_ORDERED) != null) {
            params.add(String.format("ordered={%s}", clazz.getTagByName(GenerationTagLiterals.ITM_ORDERED).getNamedParameter("Header") == null ? ""
                    : "Header"));
        }

        DocletTag importTag = clazz.getTagByName(GenerationTagLiterals.ITM_IMPORTABLE);
        if (importTag != null) {
            List<String> tmp = new LinkedList<String>();
            if (importTag.getNamedParameter("type") != null) {
                tmp.add(String.format("type=\"%s\"", importTag.getNamedParameter("type")));
            }
            if (importTag.getNamedParameter("stagingTable") != null) {
                tmp.add(String.format("stagingTable=\"%s\"", importTag.getNamedParameter("stagingTable")));
            }
            if (importTag.getNamedParameter("stagingHandler") != null) {
                tmp.add(String.format("stagingHandler=\"%s\"", importTag.getNamedParameter("stagingHandler")));
            }
            if (importTag.getNamedParameter("importOrder") != null) {
                tmp.add(String.format("importOrder=\"%s\"", importTag.getNamedParameter("importOrder")));
            }
            params.add(String.format("imports={@Import(%s)}", StringUtils.join(tmp, ",")));
        }

        if (clazz.getTagByName(GenerationTagLiterals.ITM_AUTOMATIC_KEY_GEN) != null)
            params.add(String.format("keyGen=@KeyGen(name=\"%s\", type=\"%s\", cacheSize=%s)",
                    clazz.getTagByName(GenerationTagLiterals.ITM_AUTOMATIC_KEY_GEN).getNamedParameter("name"),
                    clazz.getTagByName(GenerationTagLiterals.ITM_AUTOMATIC_KEY_GEN).getNamedParameter("type"),
                    clazz.getTagByName(GenerationTagLiterals.ITM_AUTOMATIC_KEY_GEN).getNamedParameter("cache-size")));

        if (clazz.getTagByName(GenerationTagLiterals.ITM_LABEL_DTO_SERIAL_VER_UID) != null)
            params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_LABEL_DTO_SERIAL_VER_UID,
                    "labelSerial=" + clazz.getTagByName(GenerationTagLiterals.ITM_LABEL_DTO_SERIAL_VER_UID).getNamedParameter("value")));

        if (clazz.getTagByName(GenerationTagLiterals.ITM_DTO_SERIAL_VER_UID) != null)
            params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_DTO_SERIAL_VER_UID,
                    "dtoSerial=" + clazz.getTagByName(GenerationTagLiterals.ITM_DTO_SERIAL_VER_UID).getNamedParameter("value")));

        if (clazz.getTagByName(GenerationTagLiterals.ITM_QUERY_SERIAL_VER_UID) != null)
            params.add(MergeUtils.ifTag(clazz, GenerationTagLiterals.ITM_QUERY_SERIAL_VER_UID,
                    "querySerial=" + clazz.getTagByName(GenerationTagLiterals.ITM_QUERY_SERIAL_VER_UID).getNamedParameter("value")));

        if (clazz.getTagByName(GenerationTagLiterals.ITM_FLEX_PAGE) != null) {
            List<String> pages = new LinkedList<String>();
            for (DocletTag tag : clazz.getTagsByName(GenerationTagLiterals.ITM_FLEX_PAGE)) {
                pages.add(tag.getNamedParameter("page"));
            }
            params.add(String.format("flexPages={%s}", StringMergeUtil.splitToArray(StringUtils.join(pages, ","))));
        }
        DocletTag mtdTag = clazz.getTagByName(GenerationTagLiterals.ITM_PAGE_METADATA);
        if (mtdTag != null) {
            params.add(String.format("pageMetadata=@PageMetadata(name=\"%s\", uri=\"%s\")", mtdTag.getNamedParameter("name"),
                    mtdTag.getNamedParameter("uri")));
        }

        result = StringMergeUtil.insertLine(result, "public class", StringMergeUtil.joinWithoutNulls(params, ",\n" + Constants.TAB) + "\n)");

        // TODO low priority. Add default field order...
        // itm:UI-field-order. It is
        // not used anywhere, but is generated based on the
        // default
        // value.

        return result;
    }

    private String processItmFields(List<String> itmFields, JavaMethod m) {
        itmFields.clear();
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_DELETE_CHECK, "deleteCheck=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_FLEX, "flex=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_COMPOUND_LABEL, "compoundLabel=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_CURRENCY, "currency=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_DEFAULT_ORDERBY, "defaultOrderBy=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_DELETE_ON_PARENT_DELETE, "deleteOnParentDeletion=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_EXTRACT, "extract=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_LABEL, "label=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_LOCALE_DEPENDENT, "localeDependent=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_LOCALE_KEY, "localeKey=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_NO_CHOOSER, "noChooser=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_OVERRIDE_TRANSFER_TO_EJB, "overrideTransferToEJB=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_SKIP, "skip=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_SKIP_TEST, "skipTest=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_SKIP_TRANSFER_TO_EJB, "skipTransferToEJB=true"));
        itmFields.add(MergeUtils.ifMethodTag(m, GenerationTagLiterals.ITM_PAGE_METADATA_INVISIBLE, "pageMetadataInvisible=true"));
        itmFields.add(MergeUtils.ifMethodTagValue(m, GenerationTagLiterals.ITM_CODE_TYPE, "codeType=\"%s\""));
        itmFields.add(MergeUtils.ifMethodTagValue(m, GenerationTagLiterals.ITM_PARENT_CODE_FIELD, "parentCodeField=\"%s\""));
        itmFields.add(MergeUtils.ifMethodTagValue(m, GenerationTagLiterals.ITM_EXPOSED_OBJECT_TYPE, "exposedObjectType=\"%s\""));
        itmFields.add(MergeUtils.ifMethodTagValue(m, GenerationTagLiterals.ITM_REFERENCE_TRANSFER, "referenceInTransfer=\"%s\""));
        itmFields.add(MergeUtils.ifMethodTagValue(m, GenerationTagLiterals.ITM_PRESENTATION_TYPE, "presentationType=\"%s\""));
        itmFields.add(MergeUtils.ifMethodTagValue(m, GenerationTagLiterals.ITM_TRANSFER_DTO_TYPE, "transferDTOType=\"%s\""));
        itmFields.add(MergeUtils.ifMethodTagValue(m, GenerationTagLiterals.ITM_TYPE, "type=\"%s\""));
        itmFields.add(MergeUtils.ifMethodTagValue(m, GenerationTagLiterals.ITM_USE_ALIAS, "useAlias=\"%s\""));
        // embadded annotations
        itmFields.add(MergeUtils.ifMethodTagValues(m, GenerationTagLiterals.ITM_CUSTOM_DTO, "customdto=@Dto(%s)"));
        itmFields.add(MergeUtils.ifMethodTagValue(m, GenerationTagLiterals.ITM_USAGE_TYPE, "usageType=@Usage(%s)"));
        itmFields.add(MergeUtils.ifMethodTagValue(m, GenerationTagLiterals.ITM_GRUD_PROPERTIES, "crudProperties=@CrudProp(%s)"));
        itmFields.add(MergeUtils.ifMethodTagValues(m, GenerationTagLiterals.ITM_IMPORTABLE, "importable=@Importable(%s)"));
        itmFields.add(MergeUtils.ifMethodTagValues(m, GenerationTagLiterals.ITM_BOOLEAN, "Boolean=@Boolean(%s)"));

        if (m.getTagByName(GenerationTagLiterals.ITM_SEARCHABLE_FIELD) != null) {
            String temp = "searchableField=@Searchable(searchableField=true";
            String value = m.getTagByName(GenerationTagLiterals.ITM_SEARCHABLE_FIELD).getValue();
            itmFields.add((!value.isEmpty()) ? String.format("%s,%s)", temp, value) : temp + ")");
        }
        return StringMergeUtil.joinWithoutNulls(itmFields, ",");
    }

    private void processSession(File f) throws Exception {
        MergeUtils.copyAndReplaceContents(new ContentReplacer() {
            @Override
            public String manipulate(JavaClass clazz, String string) {
                String result = StringMergeUtil.insert(string, "public class", "import javax.annotation.Resource;\r\n");
                List<String> externalFinders = new ArrayList<String>();
                if (clazz.getTagByName(GenerationTagLiterals.ITM_EXTERNAL_FINDER) != null) {
                    result = StringMergeUtil.insertLine(result, "public class", "@ItmExternalFinders({");
                    for (DocletTag finder : clazz.getTagsByName(GenerationTagLiterals.ITM_EXTERNAL_FINDER)) {
                        externalFinders.add(String.format("@ItmFinder(signature = \"%s\", factory=\"%s\", entity=\"%s\", dto=\"%s\")",
                                finder.getNamedParameter("signature"), finder.getNamedParameter("factory"), finder.getNamedParameter("entity"),
                                finder.getNamedParameter("dto")));
                    }
                    result = StringMergeUtil.insert(result, "public class", StringUtils.join(externalFinders, ",\n"));
                    result = StringMergeUtil.insertLine(result, "public class", "})");
                }
                result = MergeUtils.removeJavadocs(result);
                result = StringMergeUtil.insert(result, "SessionContext sessionContext;", "@Resource ");
                result = MergeUtils.removeCommonEjbMethods(result);
                result = MergeUtils.removeCloverTags(result);
                return result;
            }
        }, f, new File(Projects.SERVICES.getJavaSourceFolder(toRoot), StringUtils.substringAfter(f.getAbsolutePath(), "src\\java\\")));
    }

    private void processMDBs(File f) throws Exception {
        MergeUtils.copyAndReplaceContents(new ContentReplacer() {
            @Override
            public String manipulate(JavaClass clazz, String string) {
                String result = StringMergeUtil
                        .insert(string,
                                "public class",
                                "import javax.annotation.Resource;\r\n"
                                        + "import javax.ejb.ActivationConfigProperty;\r\n"
                                        + "import javax.ejb.MessageDriven;\n"
                                        + "@MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName = \"destinationType\", propertyValue = \"javax.jms.Queue\"),\n"
                                        + "@ActivationConfigProperty(propertyName = \"destination\", propertyValue = \""
                                        + clazz.getTagByName("ejbgen:message-driven").getNamedParameter("destination-jndi-name") + "\") })\n");
                result = MergeUtils.removeJavadocs(result);
                result = StringUtils.remove(result, "MessageDrivenBean,");
                result = StringMergeUtil.insert(result, "private transient MessageDrivenContext mdContext;", "    @Resource\r\n");
                result = MergeUtils.removeCommonEjbMethods(result);
                result = MergeUtils.removeCloverTags(result);
                return result;
            }
        }, f, new File(Projects.SERVICES.getJavaSourceFolder(toRoot), StringUtils.substringAfter(f.getAbsolutePath(), "src\\java\\")));
    }

    private List<String> getRelByType(JavaClass clazz, String variableName, String returnType, String relType) {
        List<String> relList = new ArrayList<String>();
        DocletTag[] relationTags = clazz.getTagsByName(relType);
        for (DocletTag relTag : relationTags) {
            if (variableName.equals(relTag.getNamedParameter("cmr-field"))) {
                relList.add(getRelAnnotation(relTag, clazz, allBeans));
                if ("Collection".equals(returnType)) {
                    returnType = String.format("Set<%s>", StringUtils.remove(relTag.getNamedParameter("target-ejb"), "Bean"));
                    relList.add("private " + returnType + " " + variableName + ";\n");
                }
            }
        }
        return relList;
    }

    private String getRelAnnotation(DocletTag relarionTag, JavaClass clazz, Map<String, JavaClass> allBeans) {
        StringBuffer result = new StringBuffer();
        String inverseFK = null;
        if ("one".equals(relarionTag.getNamedParameter(("multiplicity")))) {
            result.append("@OneToMany");
        } else {
            String targetBean = relarionTag.getNamedParameter("target-ejb");
            String relName = relarionTag.getNamedParameter("name");
            JavaClass clazzInverse = allBeans.get(targetBean);
            imports.add(StringUtils.remove(clazzInverse.getFullyQualifiedName(), "Bean"));
            DocletTag[] inverseTags = clazzInverse.getTagsByName("ejbgen:relation");
            for (DocletTag inverseTag : inverseTags) {
                if (relName.equals(inverseTag.getNamedParameter("name"))) {
                    if (!relarionTag.getValue().equals(inverseTag.getValue())) {
                        String inverseRel = StringUtils.capitalize(inverseTag.getNamedParameter("multiplicity"));
                        result.append("@ManyTo" + inverseRel);
                        inverseFK = inverseTag.getNamedParameter("fk-column");
                        break;
                    }
                }
            }
        }
        if (Boolean.valueOf(relarionTag.getNamedParameter("cascade-delete"))) {
            result.append("(cascade = CascadeType.REMOVE)");
        }
        String joinedTable = relarionTag.getNamedParameter("joint-table");
        String fk = relarionTag.getNamedParameter("fk-column");
        if (joinedTable != null) {
            result.append(String
                    .format("\n" + Constants.TAB + "@JoinTable(name = \"%s\", joinColumns = @JoinColumn(name = \"%s\"),", joinedTable, fk));
            result.append(String.format("inverseJoinColumns = @JoinColumn(name = \"%s\"))", inverseFK));
            return result.toString();
        }
        if (fk != null) {
            result.append("\n" + Constants.TAB + "@JoinColumn(");
            result.append(fk != null ? String.format("name=\"%s\")", fk) : StringUtils.EMPTY);
        }
        return result.toString();
    }
}
